package com.yuhuihui.common.aop;

import com.yuhuihui.common.annotation.result.ErrorInfo;
import com.yuhuihui.common.domain.result.ResponseResult;
import com.yuhuihui.common.domain.result.SimpleResult;
import com.yuhuihui.common.enums.ResultCodeStatusEnum;
import com.yuhuihui.common.utils.ResponseBodyInfoUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
 * controller异常替换切面
 *
 * @author yuhh
 * @date 2023-05-25 14:52:09
 */
@Aspect
public class ErrorReplaceAspect {

    /** logger */
    private static final Logger logger = LoggerFactory.getLogger(ErrorReplaceAspect.class);

    @Pointcut("@annotation(com.yuhuihui.common.annotation.result.ErrorInfo)")
    private void errorInfoPointcut(){}

    /**
     * 对有ErrorInfo注解的方法进行try catch，发生异常时根据ErrorInfo的数据返回相应的信息
     *
     * @param point 切点相关信息
     * @return {@link Object }
     * @author yuhh
     * @date 2023-05-25 14:52:17
     */
    @Around("errorInfoPointcut()")
    public Object captureExceptionAround(ProceedingJoinPoint point) {
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        assert method != null;
        // 获取该方法上的ErrorInfo注解
        ErrorInfo errorInfo = method.getAnnotation(ErrorInfo.class);
        if (method.getReturnType().isAssignableFrom(void.class)){
            this.handleThrowableVoid(point, errorInfo, method);
            return null;
        }
        return this.handleThrowableHaveResult(point, errorInfo, method);
    }

    /**
     * 处理返回类型不为void的异常
     *
     * @param point     切点
     * @param errorInfo 异常返回信息
     * @param method    切点方法
     * @return {@link Object } 返回异常数据
     * @author yuhh
     * @date 2023-05-25 14:52:21
     */
    private Object handleThrowableHaveResult(ProceedingJoinPoint point, ErrorInfo errorInfo, Method method){
        Object result;
        try {
            result = point.proceed();
        } catch (Throwable throwable) {
            this.checkReturnType(method);
            logger.error(errorInfo.errorInfo(), throwable);
            return new ResponseResult<>(ResultCodeStatusEnum.FAIL.code, errorInfo.errorMessage(), new SimpleResult(false));
        }
        return result;
    }

    /**
     * 处理返回类型为void的异常
     *
     * @param point     切点
     * @param errorInfo 异常返回信息
     * @param method    切点方法
     * @author yuhh
     * @date 2023-05-25 14:52:27
     */
    private void handleThrowableVoid(ProceedingJoinPoint point, ErrorInfo errorInfo, Method method) {
        try{
            point.proceed();
        } catch (Throwable throwable){
            logger.error(errorInfo.errorInfo(), throwable);
            ResponseBodyInfoUtil.changeMessage(method.getDeclaringClass(), method.getName(),
                    errorInfo.errorMessage(), errorInfo.errorResultCode(), false);
        }
    }

    /**
     * 检查切点方法的返回方法是否是com.yunrun.swyscloud.commons.domain.ResponseResult
     *
     * @param method 切点方法
     * @author yuhh
     * @date 2023-05-25 14:52:30
     */
    private void checkReturnType(Method method){
        if(!method.getReturnType().isAssignableFrom(ResponseResult.class)){
            throw new RuntimeException("异常信息转换返回类型转换失败,返回类型只能为void或com.yunrun.swyscloud.commons.domain.ResponseResult，其返回类型为：" + method.getReturnType().getName());
        }
    }
}
